package io.gitee.zhangbinhub.acp.cloud.tools

import feign.RequestTemplate
import io.gitee.zhangbinhub.acp.boot.component.BootLogAdapter
import io.gitee.zhangbinhub.acp.boot.tools.ServerTools
import io.gitee.zhangbinhub.acp.boot.tools.ReactiveRequestContextHolder
import io.gitee.zhangbinhub.acp.cloud.conf.AcpCloudTransmitHeaderConfiguration
import io.gitee.zhangbinhub.acp.core.common.CommonTools
import jakarta.servlet.http.HttpServletRequest
import org.springframework.beans.factory.annotation.Value
import org.springframework.cloud.commons.util.InetUtils
import org.springframework.http.HttpHeaders
import org.springframework.http.server.reactive.ServerHttpRequest
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import org.springframework.web.reactive.function.client.ClientRequest
import org.springframework.web.reactive.function.client.ExchangeFilterFunction
import reactor.core.publisher.Mono
import java.net.UnknownHostException

class CloudTools(
    private val acpCloudTransmitHeaderConfiguration: AcpCloudTransmitHeaderConfiguration,
    private val serverTools: ServerTools,
    private val inetUtils: InetUtils
) {
    @Value("\${server.address:}")
    private var ip: String? = null
    private val logAdapter = BootLogAdapter()
    fun getServerPort(): Int = serverTools.getServerPort()
    fun getServerIp(): String {
        return if (CommonTools.isNullStr(ip)) {
            try {
                inetUtils.findFirstNonLoopbackHostInfo().ipAddress ?: serverTools.getServerIp()
            } catch (e: UnknownHostException) {
                e.printStackTrace()
                ""
            }
        } else {
            ip!!
        }
    }

    private fun includeHeader(headerName: String, includeAuthorization: Boolean) =
        (includeAuthorization && headerName.equals(HttpHeaders.AUTHORIZATION, true))
                || headerName.startsWith(acpCloudTransmitHeaderConfiguration.prefix, true)

    /**
     * feign 中传递 http header 内容
     * @param requestTemplate feign请求对象
     * @param servletRequest 待传递的HttpServletRequest对象，为空时从RequestContextHolder中获取
     */
    @JvmOverloads
    fun transmitHeadersFeign(
        requestTemplate: RequestTemplate, servletRequest: HttpServletRequest? = null
    ) {
        (servletRequest ?: getServletRequest())?.let { request ->
            request.headerNames.toList().forEach { headerName ->
                if (includeHeader(headerName, true)) {
                    request.getHeaders(headerName).toList().apply {
                        if (this.isNotEmpty()) {
                            logAdapter.debug("feign[${requestTemplate.url()}] transmit header [$headerName]: $this")
                            requestTemplate.header(headerName, this)
                        }
                    }
                }
            }
        }
    }

    /**
     * restClient 中传递 http header 内容
     * @param headers 请求头
     * @param servletRequest 待传递的HttpServletRequest对象，为空时从RequestContextHolder中获取
     * @param includeAuthorization 是否包含Authorization的传递，默认true
     */
    @JvmOverloads
    fun transmitHeadersServlet(
        headers: HttpHeaders, servletRequest: HttpServletRequest? = null,
        includeAuthorization: Boolean = true
    ) {
        (servletRequest ?: getServletRequest())?.let { request ->
            request.headerNames.toList().forEach { headerName ->
                if (includeHeader(headerName, includeAuthorization)) {
                    request.getHeaders(headerName).toList().apply {
                        if (this.isNotEmpty()) {
                            logAdapter.debug("restClient transmit header [$headerName]: $this")
                            headers.addAll(headerName, this)
                        }
                    }
                }
            }
        }
    }

    /**
     * webClient 中传递 http header 内容
     * @param headers 请求头
     * @param serverHttpRequest 待传递的ServerHttpRequest对象
     * @param includeAuthorization 是否包含Authorization的传递，默认true
     */
    @JvmOverloads
    fun transmitHeadersReactive(
        headers: HttpHeaders, serverHttpRequest: ServerHttpRequest,
        includeAuthorization: Boolean = true
    ) {
        serverHttpRequest.headers.forEach { headerName, headerValues ->
            if (includeHeader(headerName, includeAuthorization)) {
                if (headerValues.isNotEmpty()) {
                    logAdapter.debug("webClient transmit header [$headerName]: $headerValues")
                    headers.addAll(headerName, headerValues)
                }
            }
        }
    }

    /**
     * webClient 中传递 http header 的过滤器
     * @param includeAuthorization 是否包含Authorization的传递，默认true
     */
    @JvmOverloads
    fun headerFilterFunction(includeAuthorization: Boolean = true): ExchangeFilterFunction =
        ExchangeFilterFunction { request, next ->
            getReactiveRequest()
                .flatMap { serverHttpRequest ->
                    ClientRequest.from(request)
                        .headers { headers ->
                            transmitHeadersReactive(
                                headers,
                                serverHttpRequest,
                                includeAuthorization
                            )
                        }
                        .build()
                        .let { next.exchange(it) }
                }
                .onErrorResume { throwable ->
                    logAdapter.error("webClient headerFilterFunction error: ${throwable.message}", throwable)
                    next.exchange(request)
                }
        }

    companion object {
        /**
         * 获取servlet请求对象
         */
        @JvmStatic
        fun getServletRequest(): HttpServletRequest? = RequestContextHolder.getRequestAttributes().let {
            if (it is ServletRequestAttributes) {
                it.request
            } else {
                null
            }
        }

        /**
         * 获取reactive请求对象
         */
        @JvmStatic
        fun getReactiveRequest(): Mono<ServerHttpRequest> = ReactiveRequestContextHolder.getRequest()
    }
}